package cn.rengy.tool.qrcode;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;

import cn.rengy.util.ExceptionPrintUtils;

/**
 * 使用ZXing，生成条码的辅助类。可以编码、解码。编码使用code包，解码需要javase包。
 */
public final class ZXingQRCode {
	private static Logger logger = LoggerFactory.getLogger(ZXingQRCode.class);
	private static final String CHARSET = "utf-8";
	//private static final int BLACK = 0xFF000000;
	//private static final int WHITE = 0xFFFFFFFF;

	// logo默认边框颜色
	public static final Color DEFAULT_BORDERCOLOR = Color.WHITE;
	// logo默认边框宽度
	public static final int DEFAULT_BORDER = 2;

	/**
	 * 禁止生成实例，生成实例也没有意义。
	 */
	private ZXingQRCode() {
	}

	/**
	 * 生成矩阵，是一个简单的函数，参数固定，更多的是使用示范。
	 * 
	 * @param text
	 * @return
	 */
	public static BitMatrix toQRCodeMatrix(String text, Integer width, Integer height) {
		// 二维码的图片格式
		Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
		// 内容所使用编码
		hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
		BitMatrix bitMatrix = null;
		try {
			bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
		} catch (Exception e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
		return bitMatrix;
	}

	/**
	 * 根据点矩阵生成黑白图。
	 */
//	public static BufferedImage toBufferedImage(BitMatrix matrix) {
//		int width = matrix.getWidth();
//		int height = matrix.getHeight();
//		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//		for (int x = 0; x < width; x++) {
//			for (int y = 0; y < height; y++) {
//				image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
//			}
//		}
//		return image;
//	}

	/**
	 * 将字符串编成一维条码的矩阵
	 * 
	 * @param str
	 * @param width
	 * @param height
	 * @return
	 */
	public static BitMatrix toBarCodeMatrix(String str, Integer width, Integer height) {
		
		try {
			// 文字编码
			Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
			hints.put(EncodeHintType.CHARACTER_SET, CHARSET);

			BitMatrix bitMatrix = new MultiFormatWriter().encode(str, BarcodeFormat.CODE_128, width, height, hints);

			return bitMatrix;
		} catch (Exception e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
		return null;
	}


	/**
	 * 将矩阵写入到输出流中。
	 */
	public static void writeToStream(String text, OutputStream stream) throws IOException {
		writeToStream(text, "png", stream, 300, 300);
	}
	/**
	 * 将矩阵写入到输出流中。
	 */
	public static void writeToFile(String text, File file) throws IOException {
		BitMatrix bitMatrix =toQRCodeMatrix( text,  300,  300);
		MatrixToImageWriter.writeToPath(bitMatrix, "png", file.toPath());
	}
	public static void writeToFile(String text, File file,int width,int height) throws IOException {
		BitMatrix bitMatrix =toQRCodeMatrix( text,  width,  height);
		MatrixToImageWriter.writeToPath(bitMatrix, "png", file.toPath());
	}
	/**
	 * 将矩阵写入到输出流中。
	 */
	public static void writeToStream(String text, String format, OutputStream stream, Integer width, Integer height)
			throws IOException {
		BitMatrix matrix = toQRCodeMatrix(text, width, height);
		BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix);
		if (!ImageIO.write(image, format, stream)) {
			throw new IOException("Could not write an image of format " + format);
		}
	}

	/**
	 * 将矩阵写入到输出流中。
	 */
	public static void writeToStreamWithLogo(String text, String format, OutputStream stream, Integer width,
			Integer height, InputStream logoIS) throws IOException {
		BitMatrix matrix = toQRCodeMatrix(text, width, height);
		BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix);
		if (null != logoIS) {
			addLogo2QRCode(image, logoIS);
		}
		if (!ImageIO.write(image, format, stream)) {
			throw new IOException("Could not write an image of format " + format);
		}
	}

	/**
	 * 给二维码图片添加Logo
	 */
	public static void addLogo2QRCode(BufferedImage image, String logoURL) {
		try {
			/**
			 * 读取Logo图片
			 */
			BufferedImage logo = ImageIO.read(new URL(logoURL));
			addLogo2QRCode(image, logo);
		} catch (IOException e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
	}

	/**
	 * 给二维码图片添加Logo
	 */
	public static void addLogo2QRCode(BufferedImage image, InputStream logoIS) {
		try {
			/**
			 * 读取Logo图片
			 */
			BufferedImage logo = ImageIO.read(logoIS);
			addLogo2QRCode(image, logo);
		} catch (IOException e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
	}

	/**
	 * 给二维码图片添加Logo
	 */
	public static void addLogo2QRCode(BufferedImage image, BufferedImage logo) {
		/**
		 * 设置logo的大小,本人设置为二维码图片的20%,因为过大会盖掉二维码
		 */
		int widthLogo = logo.getWidth(null) > image.getWidth() * 1 / 10 ? (image.getWidth() * 1 / 10)
				: logo.getWidth(null);
		int heightLogo = logo.getHeight(null) > image.getHeight() * 1 / 10 ? (image.getHeight() * 1 / 10)
				: logo.getWidth(null);

		// 计算图片放置位置
		/**
		 * logo放在中心
		 */
		int x = (image.getWidth() - widthLogo) / 2;
		int y = (image.getHeight() - heightLogo) / 2;

		/**
		 * logo放在右下角
		 */
		/*
		 * int x = (image.getWidth() - widthLogo); int y = (image.getHeight() -
		 * heightLogo);
		 */
		// 开始绘制图片
		/**
		 * 读取二维码图片，并构建绘图对象
		 */
		Graphics2D g = image.createGraphics();

		g.drawImage(logo, x, y, widthLogo, heightLogo, null);
		g.drawRoundRect(x, y, widthLogo, heightLogo, 15, 15);
		g.setStroke(new BasicStroke(DEFAULT_BORDER));
		g.setColor(DEFAULT_BORDERCOLOR);
		g.drawRect(x, y, widthLogo, heightLogo);

		g.dispose();
		logo.flush();
		image.flush();
	}

	/**
	 * 解码，需要javase包。
	 * 
	 * @param file
	 * @return
	 */
	public static String decode(File file) {
		if(file==null){
			throw new RuntimeException("文件不能为空" );
		}
		if (!file.exists() ) {
			throw new RuntimeException("文件不存在:" + file.getPath());
		}

		BufferedImage image;
		try {

			image = ImageIO.read(file);
			LuminanceSource source = new BufferedImageLuminanceSource(image);
			BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
			Result result;
			// 解码设置编码方式为：utf-8，
			Map<DecodeHintType, String> hints = new HashMap<DecodeHintType, String>();
			hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
			result = new MultiFormatReader().decode(bitmap, hints);
			return result.getText();
		} catch (Exception e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
		return null;
	}
	public static String decode(InputStream inputStream) {
		BufferedImage image;
		try {
			image = ImageIO.read(inputStream);
			LuminanceSource source = new BufferedImageLuminanceSource(image);
			BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
			Result result;
			// 解码设置编码方式为：utf-8，
			Map<DecodeHintType, String> hints = new HashMap<DecodeHintType, String>();
			hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
			result = new MultiFormatReader().decode(bitmap, hints);
			return result.getText();
		} catch (Exception e) {
			logger.error(ExceptionPrintUtils.getTrace(e));
		}
		return null;
	}
}
